home *** CD-ROM | disk | FTP | other *** search
/ CD Ware Multimedia 1995 May / cd Ware (Juegos) Epimundo.iso / DOS / C / BUMP.ZIP / BUMP.DOC < prev    next >
Encoding:
Text File  |  1991-02-10  |  19.3 KB  |  407 lines

  1.  
  2.  
  3.                                  BUMP 1.1
  4.  
  5.                 A Beginner's Understandable Matrix Package 
  6.                                in Borland C++
  7.  
  8. From the dawn of programming, people have wanted to be able to write
  9. something like
  10.  
  11.      Matrix A(4,4), B(4,4), C(4,4), D(4,4);
  12.      ...
  13.      A = (B + C)* D;
  14.  
  15. and get in the A matrix the result expected from matrix algebra.  The best
  16. that languages like Fortran, Basic, Pascal, or C could offer, however, was
  17. either a lot of function calls or loops on subscripts.  Special matrix
  18. interpreters were developed which, however, limited what you could do.  If
  19. what you wanted to do, say graph the diagonal of a matrix, couldn't be done
  20. by your package, you were stuck.  Also, you frequently had to master large
  21. manuals for one special-purpose program.   With C++, however, it is
  22. possible for the user to define a data type "Matrix" and then write
  23. functions to "overload" the =, +, and * operators so that when the compiler
  24. recognizes that, say, a + is between two matrices, it will call the user-
  25. written function to add the two matrices.  At the same time, you have all
  26. the flexibility of C and C++ and very little to learn in the way of
  27. special-purpose instructions.   It is just a matter of writing the
  28. necessary code to define the Matrix and Vector data types and to overload
  29. the operators.  
  30.  
  31. Since matrices are so widely used, I had expected that it would be easy to
  32. find examples in books of exactly how to define the data types and write
  33. the overloaded operator functions.  I found hints and fragments, but no
  34. fully worked out satisfactory solution.  In particular, the examples in the
  35. books tended to be bad about loosing track of memory that had been
  36. allocated for temporary matrices (like (B + C) in the above example), so
  37. that repeated use of equations like the one above would eventually stop the
  38. program.  Design of the destructors was of the essence for this problem,
  39. and it gets scant attention in most introductions to C++.  On the bulletin
  40. boards, I found two extensive examples at the opposite extreme, Newmat and
  41. Yamp.  They were too complicated to serve as examples.  I learned some
  42. things from them but could not begin to really follow how they worked.  So
  43. I set out on my own to see if I could apply what I thought I knew.  It was
  44. a time-consuming process full of mistakes and enigmatic compiler messages. 
  45. But the result, BUMP, seems well worth the trouble.
  46.  
  47. In BUMP you will find applications of most of the C++ techniques.  It has
  48. classes with functions, derived classes with inheritance, non-trivial
  49. constructors and destructors, overloaded operators, and even a virtual
  50. function.  In the hope that it may be useful to others both as an example
  51. and as a useable product, I have made it publicly available.  I hope that
  52. BUMP may help you over the hump to effective use of C++.
  53.  
  54. BUMP is intentionally a minimal package so that the user can understand it
  55. and extend it in directions of interest to him.  Before trying to read the
  56. code, however, it is probably a good idea to use BUMP as it is a bit so
  57. that you know what its functions do before you trying to read them.
  58. Accordingly, I'll first explain how to use it, and then add a few notes on
  59. reading the code.
  60.  
  61. Use of BUMP
  62.  
  63. To use BUMP as it is, you write a program in C++.  Don't worry if you know
  64. only C and not C++; the code you write can be pure C except for your use of
  65. BUMP's matrix and vector objects.  You must compile your program with the
  66. Borland C++ compiler, so the extension of its filename should be ".cpp". 
  67. Your program should have among the header lines the line 
  68.  
  69.  
  70.      #include "bump.h";
  71.  
  72. and the linker command should link in bump.obj.  Specific directions for
  73. compiling and linking the example are given below. 
  74.  
  75. Those simple measures are all that is necessary to be able to write a
  76. program like the following, which illustrates some of the capabilities of
  77. BUMP:
  78.  
  79.      Vector a(4),at(1,4),c(4),ct(1,4);
  80.      Matrix A(4,4),B(4,4),C(4,4),D(4,1),E(1,1);
  81.  
  82.      // Read in the matrix from an ASCII file and display it
  83.      A.ReadA("amatrix");
  84.      A.Display("Here is the A matrix:");
  85.  
  86.      // Pre-multiply it by a scalar and divide it by a scalar
  87.      B = 2.*A;
  88.      B.Display("This is 2.*A.");
  89.      B = A/2.;
  90.      B.Display("and this is A/2.");
  91.      tap();
  92.  
  93.      // Invert it. A ! in front of the matrix is the sign for inverse.
  94.      B = !A; 
  95.      B.Display("and here is B = !A ( A inverse):");
  96.  
  97.      // Multiply two matrices:
  98.      C = A*B;
  99.      // This next Display will have column width of 15 and 6 decimal places
  100.      C.Display("This is A*!A (should be I):",15,6);
  101.  
  102.      // Parentheses work as expected:
  103.      B = (A + A)*!A;
  104.      B.Display("B = (A+A)*!A. Should be 2I");
  105.  
  106.      C = A*A;
  107.      C.Display("A*A:");
  108.      C = (A+A)*(A+A);
  109.      C.Display("(A+A)*(A+A). Should be 4 times the matrix above.");
  110.  
  111.      // Take the transpose. A ~ in front of a matrix indicates transpose.
  112.      B = ~A;
  113.      A.Display("Here is A again.");
  114.      B.Display("and this is ~A, A transpose.");
  115.  
  116.      // Now some tests with vectors
  117.      a.ReadA("avector");
  118.      at = ~a;
  119.      a.Display("This is the a vector:");
  120.      at.Display("and this is its transpose. The difference doesn't show.");
  121.      D << a;
  122.      D.Display("And this is D << a :");
  123.  
  124.      c = A*a;
  125.      c.Display("This is  A*a");
  126.      ct = ~a*~A;
  127.      ct.Display("This is ~a*~A. Should look the same as the above.");
  128.  
  129.      // Tests of the / operator.  A/A is A'A computed without taking A'.
  130.      B = ~A*A;
  131.      B.Display("This is ~A*A");
  132.      B = A/A;
  133.      B.Display("This is A/A. Should be the same as the above.");
  134.  
  135.      c = A/a;
  136.  
  137.  
  138.      ct = a/A;
  139.      c.Display("This is A/a:");
  140.      ct.Display("This is a/A. Should look like the above.");
  141.  
  142.      E = a/a;
  143.      B = at/at;
  144.      E.Display("This is a/a:");
  145.      B.Display("This is at/at:");
  146.      printf("\nEnd of test.\n");
  147.      }
  148.  
  149. From this example, we see that BUMP has an extended data type Matrix.  A
  150. matrix has a function (ReadA) to read data into it from an ASCII file and
  151. another function (Display) to display the matrix on the screen.  The =,
  152. +, -, and * operators have their expected definitions.  Parentheses work in
  153. the expected way. A ~ in front of a Matrix calculates its transpose, while
  154. a ! in front of a Matrix calculates its inverse (by a primitive algorithm).
  155. Neither affects the Matrix itself. (Perhaps you don't like ~ for
  156. transposition and ! for inversion.  The ' operator, however, is not
  157. available in C++ for overloading; the ^, which I would have preferred for
  158. inversion, is a binary operator in C++ and cannot be used for a unary
  159. process like inversion or transposition.  There is, in fact, not much
  160. alternative to ~ and ! for these two unary operations.)
  161.  
  162. BUMP also has a Vector data type. All the same functions and operators
  163. (except, of course, !) apply to Vectors, and to Vectors and Matrices
  164. together, where dimensions are appropriate. 
  165.  
  166. Pre-multiplication of a Vector or Matrix by a float (a scalar) has been
  167. defined, as has division of a Matrix or Vector by a float.  I did not
  168. bother to define post-multiplication of a vector or matrix by a float.
  169.  
  170. Matrix division does not occur, so the / operator is available to mean
  171. something else.  In statistical computations, expressions like X'X or Z'X -
  172. - ~X*X or ~Z*X in BUMP notation -- occur frequently with X' or Z' being
  173. large matrices so that it undesirable to clog up scarce memory by actually
  174. taking the transpose.  These products can, of course, be computed directly
  175. from X and Z.  That is what Z/X does in BUMP; it computes ~Z*X without ever
  176. creating ~Z.
  177.  
  178.  
  179.    To convert a Vector, v, to a Matrix, M, one uses
  180.  
  181.      M << v;
  182.  
  183. Finally, element i of Vector v is denoted by v[i] on either side of the =
  184. sign, while element (i,j) of Matrix M is denoted by M[i][j] on either side
  185. of an =.  The Vector elements are range-checked on both sides of an = sign.
  186. Thus, if v has 20 elements and you ask for v[23], you will get an error
  187. message. The range checking on the Matrix element is only on the row index. 
  188. (A reference to B[i][j] actually calls the B[i] function in BUMP, which
  189. returns a pointer to the ith row of B.  The [j] then functions as
  190. subscripts normally do in C.)  You can use these functions to do anything
  191. to your matrices which BUMP does not do for you.  For example, if you want
  192. to zero out all the negative elements in the vector x, which has n elements
  193. beginning with 1, then you just do
  194.  
  195.      for(int i = 1; i <= n; i++)
  196.           if(x[i] < 0) x[i] = 0;
  197.  
  198. Thus, you have all the flexibility of C or C++, plus having a nice language
  199. for common matrix operations.  
  200.  
  201. The BUMP package includes an example program, called test.cpp.  To build
  202. it, type (at the DOS prompt) "bump test".  To run it, type "test".  When
  203.  
  204.  
  205. you have created your own test, say, "mine.cpp", you would just type "bump
  206. mine" to compile and link the program and "mine" to run it.  You will find
  207. that this process is using the bump.bat file, the bump.mak "Make" file, and
  208. bump.res "response" file for the linker.
  209.  
  210. Perhaps a word of explanation is needed as to why BUMP has the Vector data
  211. type as well as the Matrix data type.  The representation of matrices in
  212. BUMP is that used in Numerical Recipes in C, by William H. Press, et al.
  213. (Cambridge University Press).  This representation uses a vector of
  214. pointers to the rows of the matrix.  Its great advantage is that it allows
  215. arrays of more than 64K bytes to be handled by a 16-bit compiler.  It also
  216. gives the user control over whether the subscripts start at 0, 1, or
  217. whatever.  It is, however, a very inefficient way to represent a matrix
  218. which happens to be a column vector, so that it needs as many pointers to
  219. rows as it has elements.  BUMP therefore has a special data type for
  220. vectors, Vector. 
  221.  
  222. The full form for the declaration of a matrix or vector is
  223.  
  224. Matrix A(nr,nc,temp,fr,fc);
  225. Vector x(nr,nc,temp,fr,fc);
  226.  
  227. where     nr   is the number of rows (no default value);
  228.           nc   is the number of columns (default of 1 for vectors);
  229.           temp is 'y' if the object is temporary, to be destroyed by the
  230.                first operator to use it.  Objects created by an operator
  231.                all have temp set to 'y'.  This device is crucial for
  232.                throwing away the temporaries that get created in the
  233.                evaluation of expressions.  Normally, when a user declares
  234.                an object, he will want temp = 'n', not temporary, which is
  235.                the default value.  All objects are deleted if they "go out
  236.                of scope", that is, if the program exits from the function
  237.                in which they were declared.   
  238.           fr   is the first row; default is 1;
  239.           fc   is the first column; default is 1.
  240. Examples:
  241.      Matrix    A(100,5);           is a 100 X 5 non-temporary matrix with
  242.                                    first row = 1 and first column = 1.
  243.      Matrix    A(35,10,'n',60,0);  is a 35 X 10 non-temporary matrix with
  244.                                    first row = 60 and first column = 0.
  245.  
  246. BUMP has a few functions not needed in the examples above.  If A is a
  247. Vector or Matrix object, then
  248.      A.rows()            gives the number of rows.
  249.      A.columns()         gives the number of columns.
  250.      A.firstrow()        gives the first row (default = 1).
  251.      A.lastrow()         gives the last row.
  252.      A.firstcolumn()     gives the first column (default = 1).
  253.      A.lastcolumn()      gives the last column.
  254.      A.freeh()           frees all the heap memory occupied by A.  This is
  255.                          useful when you need A for several operations but
  256.                          then no longer need it and want to use the space
  257.                          it occupies for something else.  Don't try to use
  258.                          A after doing A.freeh().
  259.  
  260.      For a Matrix only, we also have
  261.      A.invert(int startrow, int endrow)
  262.           Invert the matrix and return the value of its determinant as a
  263.           double.  Note that this operation turns the Matrix A into its
  264.           inverse, whereas !A leaves A unchanged.  Start the pivot
  265.           operations in row startrow and stop when the pivot has been in
  266.           endrow.  If these arguments are omitted, the pivoting starts in
  267.           the first row and continues through the last, to produce the true
  268.           inverse.  The algorithm is Gauss-Jordan pivoting with no
  269.           niceties.  Don't trust it if your matrix poses any problems for
  270.           inversion.
  271.  
  272.  
  273.  
  274. Study of the BUMP code
  275.  
  276. To study the C++ techniques used in BUMP, you should begin with the bump.h
  277. file.  You will see that both the Vector and Matrix types are derived from
  278. a class called "vorm"  -- Vector OR Matrix.  Mostly "vorm" just has numbers
  279. of rows and columns and the like.  It does, however, contain one function,
  280. freet(), which is exactly the same for both vectors and matrices. This
  281. freet(), however, calls freeh(), which must be different for vectors and
  282. matrices.  Hence, freeh() is declared in vorm as a virtual function.  The
  283. real versions of freeh() are declared in Vector and Matrix.
  284.  
  285. The "temp" tag in the declaration of the Matrix and Vector data types is
  286. the crucial element for avoiding loss of heap memory as the program
  287. executes.  When an operator creates a matrix or vector in order to have a
  288. place to put what it computes, it sets the "temp" tag of the created object
  289. equal to 'y' to indicate that the object is temporary.  As far as I can
  290. see, temporary objects are needed only once.  Hence, whenever an operator
  291. uses a temporary object, it releases all of the heap memory allocated to it
  292. and marks it (v = 0 or m = 0) so that the automatically called destructor
  293. will not release that memory a second time.  This freeing is done by calls
  294. to freet() in the operator functions.  This freet(), declared in vorm,
  295. tests the temporary flag and, if a temporary is found, frees all the heap
  296. memory allocated object by a call to freeh().  (Hence the name "freet":
  297. FREE Temporary, while "freeh" is FREE Heap memory.)
  298.  
  299. Another crucial point to be noted is described in the long comment in
  300. Vector::Vector(Vector& a) in bump.cpp.  By watching the debugger, I learned
  301. that when a function returns an object that was originally declared in that
  302. function and is therefore "local" to the function, it automatically calls,
  303. before it returns, a constructor to copy the object that is being returned.
  304. The original will then be deleted by an automatic call to the destructor.
  305. This process, if left unchecked, will fragment the heap memory.  BUMP uses
  306. its temp tag so that the constructor detects what is happening and simply
  307. steals the content of the original local object and sets up a flag so that
  308. the destructor, to which that local object is headed, knows not to delete
  309. that content.  Thus, the fragmentation of heap memory is avoided.  I found
  310. watching this process with the debugger very helpful.  
  311.  
  312. The other thing that it took a long time for me to get into my head is the
  313. meaning of the much-used operator & in function declarations.  In
  314.  
  315.      float& Vector::operator [] (int i)
  316.  
  317. for example, what is returned is NOT a pointer to a float, but an lvalue
  318. for the float.  One of the return statements in this function is 
  319.  
  320.      return (v[i-fr]);
  321.  
  322. where v[i-fr] is just a plain old float.  It is the & in the declaration of
  323. the function that turns this float into the lvalue for the float.  Now the
  324. magic thing about lvalues is that on the right of an = sign they are just
  325. numbers, but on the left of an = they are the place the number is stored. 
  326. Thus, if we have
  327.  
  328.      Vector A(10);
  329.      float x;
  330.  
  331.      x = 2.;
  332. then
  333.      x = A[6];
  334. will call the float& Vector::operator [] (int i) function to get the value
  335. of the sixth element of A to put into x, while
  336.  
  337.      A[6] = x;
  338.  
  339. calls exactly the same function to get the address at which to store x so
  340. that it will be the sixth element of A.  Such is the magic of the lvalue
  341. and the & operator. 
  342.  
  343.  
  344.  
  345. In the storage of matrices, I have used exactly the device from Numerical
  346. Recipes in C.  In a Matrix with fr and fc (first row and first column) both
  347. 1, m[1][1] will be the very first piece of storage allocated for the
  348. matrix.  In that case, m[0][0] would be outside the range of m and
  349. reference to it would be an error.  I did not follow this convention for
  350. Vectors, where the first element is always in v[0].  I considered switching
  351. over the Vector convention to the Matrix convention, but found that to do
  352. so would simplify only 6 expressions and would complicate or at least not
  353. simplify 31 expressions.  So I did not switch. 
  354.  
  355.                                    * * *
  356.  
  357. BUMP is public domain.  If you find bugs, I would like to know about them. 
  358. My own intentions are to leave BUMP pretty much as it is except for bug
  359. fixes.  I will be working on integrating it into other programs and will
  360. probably develop a facility for sparse matrices, but this will not go into
  361. BUMP, which needs to be left simple enough that the neophyte -- like me --
  362. can understand it.  
  363.  
  364.      Clopper Almon
  365.      Department of Economics
  366.      University of Maryland
  367.      College Park, MD 20742
  368.      Internet: CA10@umail.umd.edu
  369.      Compuserve: 73377,1466
  370.  
  371.  
  372.  
  373.          ----------------end-of-author's-documentation---------------
  374.  
  375.                          Software Library Information:
  376.  
  377.                     This disk copy provided as a service of
  378.  
  379.                            Public (software) Library
  380.  
  381.          We are not the authors of this program, nor are we associated
  382.          with the author in any way other than as a distributor of the
  383.          program in accordance with the author's terms of distribution.
  384.  
  385.          Please direct shareware payments and specific questions about
  386.          this program to the author of the program, whose name appears
  387.          elsewhere in  this documentation. If you have trouble getting
  388.          in touch with the author,  we will do whatever we can to help
  389.          you with your questions. All programs have been tested and do
  390.          run.  To report problems,  please use the form that is in the
  391.          file PROBLEM.DOC on many of our disks or in other written for-
  392.          mat with screen printouts, if possible.  PsL cannot debug pro-
  393.          programs over the telephone, though we can answer questions.
  394.  
  395.          Disks in the PsL are updated  monthly,  so if you did not get
  396.          this disk directly from the PsL, you should be aware that the
  397.          files in this set may no longer be the current versions. Also,
  398.          if you got this disk from another vendor and are having prob-
  399.          lems,  be aware that  some files may have become corrupted or
  400.          lost by that vendor. Get a current, working disk from PsL.
  401.  
  402.          For a copy of the latest monthly software library newsletter
  403.          and a list of the 4,000+ disks in the library, call or write
  404.  
  405.                            Public (software) Library
  406.                                P.O.Box 35705 - F
  407.                             Houston, TX 77235-5705
  408.  
  409.                                 1-800-2424-PSL
  410.                              MC/Visa/AmEx/Discover
  411.  
  412.                           Outside of U.S. or in Texas
  413.                           or for general information,
  414.                               Call 1-713-524-6394
  415.  
  416.                           PsL also has an outstanding
  417.                           catalog for the Macintosh.
  418.  
  419.